package checkers3d.presentation;

import checkers3d.logic.GameBase;
import checkers3d.logic.GameMaster;
import checkers3d.logic.Player;
import checkers3d.logic.PlayingSurface3D;
import java.awt.Point;
import java.util.LinkedList;

/**
 * This class displays a view of one PlayingSurface3D object from a list. A list
 * is passed containing, in order, an archive of previous game states. By default
 * the user views the first game state but is offered controls to move backwards
 * and forwards in time.
 *
 * @author Ruben Acuna
 */
public class InstantReplayWindow extends GameBase
{
    /**
     * A list of game states that are viewable.
     */
    private LinkedList<PlayingSurface3D> replays = new LinkedList<PlayingSurface3D>();

    /**
     * How many milliseconds ago the the game state was advanced automatically.
     */
    private int animationLastFrameTime = 0;

    /**
     * Indicates if play mode is on.
     */
    private boolean animationOn = false;

    /**
     * How often (in milliseconds) the game state should advance in play mode.
     */
    private final int animationRate = 500;

    /**
     * Position of the view states control bar.
     */
    private final Point POSITION_CONTROLS = new Point (GameEngine.WINDOW_SIZE.x - 400,
                                                       GameEngine.WINDOW_SIZE.y - 100);

    /**
     * Creates a window for viewing the PlayingSurface3Ds and adds a menu bar
     * with controls for changing the current state.
     *
     * @param home The home Player - used only for GameBase.
     * @param visitor The visiting Player - used only for GameBase.
     * @param replays
     * @param size The size of the window.
     */
    public InstantReplayWindow(Player home, Player visitor, LinkedList<PlayingSurface3D> replays, Point size)
    {
        super(home, visitor, visitor, replays.getFirst(), size);

        this.replays = replays;

        //the values added to the positions of controls is the button width plus
        //a pad: (64 + 32)*distance from first button. e.g (64+32)*1=96, etc.
        add(new GUIButton("button-ir-backward.png", POSITION_CONTROLS, new ObserverBackward(this)));
        add(new GUIButton("button-ir-stop.png", new Point (POSITION_CONTROLS.x + 96, POSITION_CONTROLS.y), new ObserverStop(this)));
        add(new GUIButton("button-ir-play.png", new Point (POSITION_CONTROLS.x + 192, POSITION_CONTROLS.y), new ObserverPlay(this)));
        add(new GUIButton("button-ir-forward.png", new Point (POSITION_CONTROLS.x + 288, POSITION_CONTROLS.y), new ObserverForward(this)));
    }

    /**
     * Handles a key press, by sending it to ll of its components. If escape is
     * pressed, the window is closed. If space is pressed, the state advances.
     *
     * @param  key       The character of the key that was pressed.
     * @see IInputObserver
     */
    @Override
    public void onKeyPress(char key)
    {
        int code = (int)key;
        
        if(code == UtilGUI.KEY_ESCAPE)
            GameMaster.getInstance().removeTopGUIContainer();
        else if(code == UtilGUI.KEY_SPACE)
            playNext();
    }

    /**
     * Informs the GUIContainer that a given amount of time in milliseconds has
     * elapsed. If play is on, the frame may be advanced.
     *
     * @param ms Milliseconds elapsed.
     */
    @Override
    public void tick(int ms)
    {
        if(animationOn)
        {
            animationLastFrameTime += ms;

            //check if it is time to change frame.
            if(animationLastFrameTime >= animationRate)
            {
                stepNext();

                animationLastFrameTime = 0;
            }
        }
    }

    /**
     * Advances the currently displayed PlayingSurface3D one move forward.
     */
    private void stepNext()
    {
        int currentPlayIndex = replays.indexOf(playingSurface3D);

        if(currentPlayIndex < (replays.size() - 1))
        {
            playingSurface3D = replays.get(currentPlayIndex + 1);
            refreshBoardRR();
        }
    }

    /**
     * Advances the currently displayed PlayingSurface3D one move backward.
     */
    private void stepPrevious()
    {
        int currentPlayIndex = replays.indexOf(playingSurface3D);

        if(currentPlayIndex > 0)
        {
            playingSurface3D = replays.get(currentPlayIndex - 1);
            refreshBoardRR();
        }
    }

    /**
     * Updates the render resources used in GameBase to match the current
     * PlayingSurface3D.
     */
    private void refreshBoardRR()
    {
        int currentBoard = 0;

        for(IDrawable drawable : getComponentsList())
            if(drawable instanceof GUIPlayingSurface2D)
            {
                //HACK: we assume that the boards were added in the order 0..k,
                //      in order to predict the indices.
                ((GUIPlayingSurface2D)drawable).setRenderResource(new RenderResourcePlayingSurface2D(playingSurface3D.getSurface2D(currentBoard), TILE_SIZE));
                currentBoard++;
            }
    }

    /**
     * Advances the frame forward if animation is not on. If animation is on,
     * it turns it off.
     */
    public void playNext()
    {
        if(!animationOn)
            stepNext();
        else
            animationOn = false;
    }

    /**
     * Advances the frame backward if animation is not on. If animation is on,
     * it turns it off.
     */
    public void playPrevious()
    {
        if(!animationOn)
            stepPrevious();
        else
            animationOn = false;
    }

    /**
     * Starts state animation.
     */
    public void playStart()
    {
        animationOn = true;
    }

    /**
     * Stops state animation.
     */
    public void playStop()
    {
        animationOn = false;
    }

   /**
     * An observer for the forward button to advance the state.
     */
    private class ObserverForward implements IInputObserver
    {
        /**
         * Reference to the parent window.
         */
        InstantReplayWindow parent;

        /**
         * Class constructor that records the parent window.
         *
         * @param parent Parent window
         */
        public ObserverForward(InstantReplayWindow parent)
        {
            this.parent = parent;
        }

        /**
         * Performs button action.
         *
         * @param position Location of click within button.
         */
        public void onClick(Point position)
        {
            parent.playNext();
        }

        /**
         * Key press observer that does nothing.
         *
         * @param key Key that was pressed.
         */
        public void onKeyPress(char key)
        {
            //do nothing.
        }
    }

   /**
     * An observer for the backward button to rewind the state.
     */
    private class ObserverBackward implements IInputObserver
    {
        /**
         * Reference to the parent window.
         */
        InstantReplayWindow parent;

        /**
         * Class constructor that records the parent window.
         *
         * @param parent Parent window
         */
        public ObserverBackward(InstantReplayWindow parent)
        {
            this.parent = parent;
        }

        /**
         * Performs button action.
         *
         * @param position Location of click within button.
         */
        public void onClick(Point position)
        {
            parent.playPrevious();
        }

         /**
         * Key press observer that does nothing.
         *
         * @param key Key that was pressed.
         */
        public void onKeyPress(char key)
        {
            //do nothing.
        }
    }

   /**
     * An observer for the play button to advance to start state animation.
     */
    private class ObserverPlay implements IInputObserver
    {
        /**
         * Reference to the parent window.
         */
        InstantReplayWindow parent;

        /**
         * Class constructor that records the parent window.
         *
         * @param parent Parent window
         */
        public ObserverPlay(InstantReplayWindow parent)
        {
            this.parent = parent;
        }

        /**
         * Performs button action.
         *
         * @param position Location of click within button.
         */
        public void onClick(Point position)
        {
            parent.playStart();
        }

        /**
         * Key press observer that does nothing.
         *
         * @param key Key that was pressed.
         */
        public void onKeyPress(char key)
        {
            //do nothing.
        }
    }

   /**
     * An observer for the stop button to advance to stop state animation.
     */
    private class ObserverStop implements IInputObserver
    {
        /**
         * Reference to the parent window.
         */
        InstantReplayWindow parent;

        /**
         * Class constructor that records the parent window.
         *
         * @param parent Parent window
         */
        public ObserverStop(InstantReplayWindow parent)
        {
            this.parent = parent;
        }

        /**
         * Performs button action.
         *
         * @param position Location of click within button.
         */
        public void onClick(Point position)
        {
            parent.playStop();
        }

        /**
         * Key press observer that does nothing.
         *
         * @param key Key that was pressed.
         */
        public void onKeyPress(char key)
        {
            //do nothing.
        }
    }
 
}
